/*
 * Copyright 2011 Christoph Ludwig (christoph[at]chludwig.de). All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package de.chludwig.ehumanities.provenance.resources;

import java.net.URI;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutorService;

import javax.servlet.ServletContext;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import javax.xml.bind.JAXBElement;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.chludwig.ehumanities.provenance.background.BackgroundThreadContextListener;
import de.chludwig.ehumanities.provenance.entities.LogEntry;
import de.chludwig.ehumanities.provenance.entities.WorkflowContainer;
import de.chludwig.ehumanities.provenance.jaxb.EntryType;
import de.chludwig.ehumanities.provenance.jaxb.LogEntryMetadataType;
import de.chludwig.ehumanities.provenance.jaxb.LogEntryType;
import de.chludwig.ehumanities.provenance.jaxb.ObjectFactory;
import de.chludwig.ehumanities.provenance.jaxb.WorkflowContainerMetadataType;
import de.chludwig.ehumanities.provenance.jaxb.WorkflowContainerType;
import de.chludwig.ehumanities.provenance.jaxb.WorkflowContainerType.Entries;

/**
 * Resource that represents a {@link WorkflowContainer}.
 */
@Path("/container/{workflowContainerId}")
public class WorkflowContainerResource {

  public static final String ENTRY_PATH_PREFIX = "/entry";
  public static final String ENTRY_PATH_ID = "/{logEntryId}";
  public static final String ENTRY_PATH = ENTRY_PATH_PREFIX + ENTRY_PATH_ID;

  public static final String ZIP_PATH = "/zip";

  private static final Logger LOG = LoggerFactory.getLogger(WorkflowContainerResource.class);

  @Context
  private UriInfo uriInfo;

  @Context
  private ServletContext context;

  private WorkflowContainer workflowContainer;

  /**
   * @return the uriInfo
   */
  public UriInfo getUriInfo() {
    return uriInfo;
  }

  /**
   * @return the workflowContainer
   */
  public WorkflowContainer getWorkflowContainer() {
    return workflowContainer;
  }

  /**
   * @param workflowContainer
   *          the workflowContainer to set
   */
  protected void setWorkflowContainer(WorkflowContainer workflowContainer) {
    this.workflowContainer = workflowContainer;
  }

  /**
   * @param containerId
   */
  protected void setWorkflowContainer(UUID containerId) {
    WorkflowContainer container = WorkflowContainer.containerWithId(containerId);
    setWorkflowContainer(container);
  }

  @GET
  @Produces(MediaType.APPLICATION_XML)
  public Response getWorkflowContainerXML(@PathParam("workflowContainerId") UUID containerId) {
    setWorkflowContainer(containerId);

    WorkflowContainerMetadataType workflowContainerMetadata = getWorkflowContainer().getMetadata();

    WorkflowContainerType container = new WorkflowContainerType();
    container.setMetadata(workflowContainerMetadata);
    container.setEntries(getEntries());
    JAXBElement<WorkflowContainerType> responseEntity = new ObjectFactory().createWorkflowContainer(container);
    CacheControl noCache = ProvenanceApplication.noCacheControl();
    LOG.info("GET [{}] container {}", MediaType.APPLICATION_XML, containerId);
    return Response.ok(responseEntity, MediaType.APPLICATION_XML).cacheControl(noCache).build();
  }

  /**
   * @return
   */
  private Entries getEntries() {
    Entries containerEntries = new WorkflowContainerType.Entries();
    List<EntryType> entryList = containerEntries.getEntry();
    UriBuilder entryUriBuilder = getUriInfo().getRequestUriBuilder().path(ENTRY_PATH);
    UriBuilder dataUriBuilder = entryUriBuilder.clone().path(LogEntryResource.DATA_PATH);

    for (LogEntry logEntry : getWorkflowContainer().getLogEntries()) {
      EntryType entry = entryTypeFromLogEntry(entryUriBuilder, dataUriBuilder, logEntry);
      entryList.add(entry);
    }

    return containerEntries;
  }

  /**
   * @param entryUriBuilder
   * @param dataUriBuilder
   * @param logEntry
   * @return
   */
  public EntryType entryTypeFromLogEntry(UriBuilder entryUriBuilder, UriBuilder dataUriBuilder, LogEntry logEntry) {
    LogEntryMetadataType entryMetadata = logEntry.getMetadata();
    String entryId = entryMetadata.getUuid().toString();

    LogEntryType logEntryDescriptor = new LogEntryType();
    logEntryDescriptor.setMetadata(entryMetadata);
    logEntryDescriptor.setDataLocator(dataUriBuilder.build(entryId).toASCIIString());

    EntryType entry = new EntryType();
    entry.setEntry(logEntryDescriptor);
    entry.setLocator(entryUriBuilder.build(entryId).toASCIIString());
    return entry;
  }

  @POST
  @Path(ENTRY_PATH_PREFIX)
  @Produces(MediaType.TEXT_PLAIN)
  public Response newLogEntry(@PathParam("workflowContainerId") UUID containerId) {
    setWorkflowContainer(containerId);

    UUID entryId = UUID.randomUUID();
    LogEntry.createNewLogEntry(getWorkflowContainer(), entryId);
    
    URI entryUri = uriInfo.getAbsolutePathBuilder().path(ENTRY_PATH_ID).build(entryId.toString());
    LOG.info("POST on container {} created entry {}", containerId, entryId);
    return Response.created(entryUri).build();
  }

  @POST
  @Path(ZIP_PATH)
  public Response zipContainer(@PathParam("workflowContainerId") UUID containerId) {
    setWorkflowContainer(containerId);
    
    ExecutorService executor = (ExecutorService) context.getAttribute(BackgroundThreadContextListener.BACKGROUND_THREAD_EXECUTOR);
    getWorkflowContainer().moveToArchive(executor);
    URI zippingLocator = getUriInfo().getBaseUriBuilder().path(ZipInProgressResource.class)
            .build(containerId.toString());
    LOG.info("POST on container {} turned it into an archive");
    return Response.status(Status.ACCEPTED).location(zippingLocator).build();
  }

  @Path(ENTRY_PATH)
  public LogEntryResource getLogEntryResource(@PathParam("workflowContainerId") UUID containerId,
          @PathParam("logEntryId") UUID logEntryId) {
    setWorkflowContainer(containerId);
    LogEntry logEntry = LogEntry.existingLogEntry(getWorkflowContainer(), logEntryId);
    return new LogEntryResource(logEntry);
  }
}
